// Copyright (c) 2019 Presto Labs Pte. Ltd.
// Author: xguo, gekim

#pragma once

#include <algorithm>
#include <chrono>
#include <experimental/filesystem>
#include <fstream>
#include <functional>
#include <iostream>
#include <optional>
#include <sstream>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include <numeric>


#include "coin2/base/log.h"

inline bool StringStartsWith(std::string_view hay, std::string_view needle) {
  return hay.find(needle) == 0;
}

inline bool StringEndsWith(std::string_view hay, std::string_view needle) {
  return hay.size() >= needle.size() &&
         hay.compare(hay.size() - needle.size(), needle.size(), needle.data()) == 0;
}

inline bool StringIsUpper(const std::string& str) {
  return std::all_of(str.begin(), str.end(), ::isupper);
}

inline bool StringIsLower(const std::string& str) {
  return std::all_of(str.begin(), str.end(), ::islower);
}

inline std::string* StringToUpperInPlace(std::string* str) {
  std::transform(str->begin(), str->end(), str->begin(), ::toupper);
  return str;
}

inline std::string* StringToLowerInPlace(std::string* str) {
  std::transform(str->begin(), str->end(), str->begin(), ::tolower);
  return str;
}

inline std::string StringToUpperCopy(const std::string& str) {
  std::string res(str);
  return *StringToUpperInPlace(&res);
}

inline std::string StringToLowerCopy(const std::string& str) {
  std::string res(str);
  return *StringToLowerInPlace(&res);
}

inline std::vector<std::string> Split(std::string_view sv, std::string_view delims) {
  std::vector<std::string> out;
  while (true) {
    if (sv.empty()) {
      out.emplace_back(sv);
      break;
    }
    int idx = sv.find_first_of(delims);
    if (idx == std::string_view::npos) {
      out.emplace_back(sv);
      break;
    } else {
      out.emplace_back(sv.substr(0, idx));
      sv = sv.substr(idx + 1);
    }
  }
  return out;
}

inline std::string ReadFileContent(const std::experimental::filesystem::path& filename) {
  if (!std::experimental::filesystem::exists(filename)) {
    return "";
  }
  std::ifstream infile(filename);
  if (!infile.good()) {
    LOG(ERROR) << "couldn't read the file: " << filename;
  }
  std::stringstream buffer;
  buffer << infile.rdbuf();
  return buffer.str();
}

inline std::string Join(const std::vector<std::string>& words,
                        const std::string delimiter = ",") {
  return std::accumulate(
      std::cbegin(words), std::cend(words), std::string(),
      [&delimiter](const std::string& ss, const std::string& s) {
        return ss.empty() ? s : ss + delimiter + s;
      });
}
